package com.yupi.springbootinit.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.cron.TaskExecutor;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.Gson;
import com.yupi.springbootinit.annotation.AuthCheck;
import com.yupi.springbootinit.bizmq.BiMessageProducer;
import com.yupi.springbootinit.bizmq.BiMqConstant;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.DeleteRequest;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.constant.FileConstant;
import com.yupi.springbootinit.constant.UserConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.manager.AiManager;
import com.yupi.springbootinit.manager.RedisLimiterManager;
import com.yupi.springbootinit.model.dto.chart.*;
import com.yupi.springbootinit.model.dto.file.UploadFileRequest;
import com.yupi.springbootinit.model.entity.Chart;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.enums.FileUploadBizEnum;
import com.yupi.springbootinit.model.vo.BiResponse;
import com.yupi.springbootinit.service.ChartService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.ChatAiUtils;
import com.yupi.springbootinit.utils.ExcelUtils;
import com.yupi.springbootinit.utils.SqlGeneratorUtils;
import com.yupi.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 帖子接口
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://yupi.icu">编程导航知识星球</a>
 */
@RestController
@RequestMapping("/chart")
@Slf4j
public class ChartController {

    @Resource
    private ChartService chartService;

    @Resource
    private UserService userService;

    @Resource
    private AiManager aiManager;

    @Resource
    private RedisLimiterManager redisLimiterManager;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;// 注入线程池

    @Resource
    private BiMessageProducer biMessageProducer;

//    @Resource
//    private B

    // 注入SiliconFlow API Key
    @Value("${siliconflow.api-key}")
    private String siliconFlowApiKey;

    // 注入SiliconFlow模型名称
    @Value("${siliconflow.model}")
    private String siliconFlowModel;

    private final static Gson GSON = new Gson();

    // region 增删改查

    /**
     * 创建
     *
     * @param chartAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addChart(@RequestBody ChartAddRequest chartAddRequest, HttpServletRequest request) {
        if (chartAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Chart chart = new Chart();
        BeanUtils.copyProperties(chartAddRequest, chart);
        User loginUser = userService.getLoginUser(request);
        chart.setUserId(loginUser.getId());
        boolean result = chartService.save(chart);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        long newChartId = chart.getId();
        return ResultUtils.success(newChartId);
    }

    /**
     * 删除
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteChart(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Chart oldChart = chartService.getById(id);
        ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
        if (!oldChart.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean b = chartService.removeById(id);
        return ResultUtils.success(b);
    }

    /**
     * 更新（仅管理员）
     *
     * @param chartUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateChart(@RequestBody ChartUpdateRequest chartUpdateRequest) {
        if (chartUpdateRequest == null || chartUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Chart chart = new Chart();
        BeanUtils.copyProperties(chartUpdateRequest, chart);
        long id = chartUpdateRequest.getId();
        // 判断是否存在
        Chart oldChart = chartService.getById(id);
        ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
        boolean result = chartService.updateById(chart);
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 获取
     *
     * @param id
     * @return
     */
    @GetMapping("/get")
    public BaseResponse<Chart> getChartById(long id, HttpServletRequest request) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Chart chart = chartService.getById(id);
        if (chart == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        return ResultUtils.success(chart);
    }

    /**
     * 分页获取列表（封装类）
     *
     * @param chartQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page")
    public BaseResponse<Page<Chart>> listChartByPage(@RequestBody ChartQueryRequest chartQueryRequest,
                                                     HttpServletRequest request) {
        long current = chartQueryRequest.getCurrent();
        long size = chartQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        Page<Chart> chartPage = chartService.page(new Page<>(current, size),
                getQueryWrapper(chartQueryRequest));
        return ResultUtils.success(chartPage);
    }

    /**
     * 分页获取当前用户创建的资源列表
     *
     * @param chartQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/my/list/page")
    public BaseResponse<Page<Chart>> listMyChartByPage(@RequestBody ChartQueryRequest chartQueryRequest,
                                                       HttpServletRequest request) {
        if (chartQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        chartQueryRequest.setUserId(loginUser.getId());
        long current = chartQueryRequest.getCurrent();
        long size = chartQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        Page<Chart> chartPage = chartService.page(new Page<>(current, size),
                getQueryWrapper(chartQueryRequest));
        return ResultUtils.success(chartPage);
    }

    // endregion

    /**
     * 编辑（用户）
     *
     * @param chartEditRequest
     * @param request
     * @return
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editChart(@RequestBody ChartEditRequest chartEditRequest, HttpServletRequest request) {
        if (chartEditRequest == null || chartEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Chart chart = new Chart();
        BeanUtils.copyProperties(chartEditRequest, chart);
        User loginUser = userService.getLoginUser(request);
        long id = chartEditRequest.getId();
        // 判断是否存在
        Chart oldChart = chartService.getById(id);
        ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可编辑
        if (!oldChart.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean result = chartService.updateById(chart);
        return ResultUtils.success(result);
    }


    /**
     * 智能分析
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
//    @PostMapping("/gen")
//    public BaseResponse<BiResponse> genChartByAI(@RequestPart("file") MultipartFile multipartFile,
//                                             GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
//
//
//        // 使用 getter 方法获取属性值
//        String name = genChartByAiRequest.getName();
//        String goal = genChartByAiRequest.getGoal();
//        String chartType = genChartByAiRequest.getChartType();
//
//        //校验
//        ThrowUtils.throwIf(StringUtils.isBlank( goal),ErrorCode.PARAMS_ERROR,"目标为空");
//        ThrowUtils.throwIf(StringUtils.isNotBlank( name) && name.length() >100,ErrorCode.PARAMS_ERROR,"名称过长");
//
//        // 用户输入
//        StringBuilder userInput = new StringBuilder();
//        userInput.append("分析目标：").append(goal).append("\n");
//        if (StringUtils.isNotBlank(chartType)) {
//            userInput.append("图表类型：").append(chartType).append("\n");
//        }
//
//        // 读取到用户上传的excel文件，进行一个处理
//        String csvData = ExcelUtils.excelToCsv(multipartFile);
//        userInput.append("数据：").append(csvData).append("\n");
//
//        // 调用AI
//        String result = aiManager.doChat(Arrays.asList(
//                ChatAiUtils.Message.system("你是一个数据分析师和前端开发专家。请严格按照以下要求处理用户提供的数据：\n\n" +
//                        "1. 输入格式：\n" +
//                        "分析需求：\n" +
//                        "{数据分析的需求或者目标}\n" +
//                        "原始数据：\n" +
//                        "{csv格式的原始数据，用,作为分隔符}\n\n" +
//                        "2. 输出格式要求（严格按照以下格式输出，不要有任何额外内容）：\n" +
//                        "``【【【【【\n" +
//                        "{前端 Echarts V5 的 option 配置对象js代码，合理地将数据进行可视化，不要生成任何多余内容}\n" +
//                        "``【【【【【\n" +
//                        "{明确的数据分析结论、越详细越好，不要生成多余的注释}\n\n" +
//                        "3. 示例问答：\n\n" +
//                        "输入示例：\n" +
//                        "分析需求：\n" +
//                        "分析网站用户访问趋势，找出访问量最高的日期并分析增长趋势\n" +
//                        "原始数据：\n" +
//                        "日期,访问量\n" +
//                        "2023-10-01,1200\n" +
//                        "2023-10-02,1500\n" +
//                        "2023-10-03,1800\n" +
//                        "2023-10-04,1600\n" +
//                        "2023-10-05,2100\n" +
//                        "2023-10-06,2400\n" +
//                        "2023-10-07,2200\n\n" +
//                        "输出示例：\n" +
//                        "``【【【【【\n" +
//                        "option = {\n" +
//                        "    title: {\n" +
//                        "        text: '网站用户访问趋势'\n" +
//                        "    },\n" +
//                        "    tooltip: {},\n" +
//                        "    legend: {\n" +
//                        "        data:['访问量']\n" +
//                        "    },\n" +
//                        "    xAxis: {\n" +
//                        "        type: 'category',\n" +
//                        "        data: ['2023-10-01', '2023-10-02', '2023-10-03', '2023-10-04', '2023-10-05', '2023-10-06', '2023-10-07']\n" +
//                        "    },\n" +
//                        "    yAxis: {},\n" +
//                        "    series: [{\n" +
//                        "        name: '访问量',\n" +
//                        "        type: 'line',\n" +
//                        "        data: [1200, 1500, 1800, 1600, 2100, 2400, 2200]\n" +
//                        "    }]\n" +
//                        "};\n" +
//                        "``【【【【【\n" +
//                        "根据提供的网站访问数据，分析结果如下：\n" +
//                        "1. 数据概览：数据涵盖了7天的访问量，访问量在1200到2400之间波动。\n" +
//                        "2. 趋势分析：整体呈现上升趋势，从第一天的1200增长到第六天的2400，增长率达到100%。\n" +
//                        "3. 最高访问量：第六天（2023-10-06）达到最高访问量2400。\n" +
//                        "4. 增长模式：前三天持续增长，第四天略有下降，之后继续增长并在第六天达到峰值，第七天稍有回落。\n" +
//                        "5. 建议：可以进一步分析第六天访问量激增的原因，以便在未来复制成功经验。"),
//                ChatAiUtils.Message.user(userInput.toString())
//        ));
//        String[] split = result.split("``【【【【【\n");
//        if(split.length < 3){
//            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"生成格式错误");
//        }
//        String genChart = split[1];
//        String genResult = split[2];
//        BiResponse biResponse = new BiResponse();
//        biResponse.setGenChart(genChart);
//        biResponse.setGenResult(genResult);
//        return ResultUtils.success(biResponse);
//
//    }

    /**
     * 智能分析同步
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @PostMapping("/gen")
    public BaseResponse<BiResponse> genChartByAI(
            @RequestPart("file") MultipartFile multipartFile,
            GenChartByAiRequest genChartByAiRequest,
            HttpServletRequest request) {

        // 1. 获取请求参数
        String name = genChartByAiRequest.getName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();

        // 2. 参数校验
        ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "分析目标不能为空");
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "图表名称不能超过100个字符");

        //校验文件
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename();
        User loginUser = userService.getLoginUser(request);

        //限流, 用户每秒限流 5 个请求
        redisLimiterManager.doRateLimit("genChartByAi_" + loginUser.getId());
        //校验文件大小
        final long fifty_MB = 1024 * 1024 * 50L;
        ThrowUtils.throwIf(fileSize > fifty_MB, ErrorCode.PARAMS_ERROR, "文件大小不能超过50MB");
        //校验文件后缀
        ThrowUtils.throwIf(!Arrays.asList("xlsx", "xls", "csv").contains(FileUtil.getSuffix(fileName)), ErrorCode.PARAMS_ERROR, "文件类型错误");

        // 3. 构造用户输入（用于发送给 AI）
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求：\n").append(goal).append("\n");
        if (StringUtils.isNotBlank(chartType)) {
            userInput.append("图表类型：").append(chartType).append("\n");
        }

        String csvData;
// 判断文件类型，如果是csv文件则直接读取，否则通过Excel转换
        if ("csv".equals(FileUtil.getSuffix(fileName))) {
            try {
                csvData = new String(multipartFile.getBytes(), "UTF-8");
            } catch (Exception e) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "CSV文件读取失败");
            }
        } else {
            csvData = ExcelUtils.excelToCsv(multipartFile);
        }
        userInput.append("原始数据：\n").append(csvData);

        // 4. 定义系统提示词（优化后的 Prompt）
        String systemPrompt = "你是一个专业的数据分析师和前端可视化专家，请根据用户提供的分析需求和原始数据，完成以下任务：\n\n" +
                "### 输入格式\n" +
                "分析需求：\n" +
                "{目标描述}\n" +
                "图表类型（可选）：\n" +
                "{line, bar, pie 等}\n" +
                "原始数据：\n" +
                "{CSV 格式数据，以逗号分隔}\n\n" +

                "### 输出格式要求（必须严格遵守！）\n" +
                "请按顺序输出以下两个部分，使用标准 Markdown 代码块包裹，禁止添加任何解释、标题或额外文本：\n\n" +

                "``json\n" +
                "{ECharts V5 的 option 配置对象，必须是严格的JSON格式，所有字符串必须使用双引号，不要使用单引号或任何JavaScript语法！}\n" +
                "```\n\n" +

                "``text\n" +
                "{详细的数据分析结论，越详细越好，包含趋势、极值、异常、建议等，不要出现代码}\n" +
                "```\n\n" +

                "### 重要规则说明\n" +
                "- 必须先输出 ECharts 配置，再输出分析结论。\n" +
                "- 必须使用上述代码块标记（```json 和 ```text），不要使用其他符号。\n" +
                "- 禁止输出任何额外内容（如“好的”、“已生成”、“注意：”等引导语）。\n" +
                "- ECharts配置必须是严格的JSON格式，所有字符串必须使用双引号\"\"而不是单引号''！\n" +
                "- option 中不要包含 console.log、click 事件或其他副作用代码。\n" +
                "- 图表标题应简洁明确，反映分析目标。\n" +
                "- 自动识别异常值或突变点并在分析中说明。\n" +
                "- 如果遇到无法处理的请求，直接返回：`AI处理失败：{具体错误原因}`\n\n" +

                "### 示例格式（注意JSON格式和双引号的使用）\n" +
                "正确的JSON格式示例：\n" +
                "``json\n" +
                "{\n" +
                "  \"title\": {\n" +
                "    \"text\": \"用户增长趋势\",\n" +
                "    \"left\": \"center\"\n" +
                "  },\n" +
                "  \"tooltip\": {\n" +
                "    \"trigger\": \"axis\"\n" +
                "  },\n" +
                "  \"xAxis\": {\n" +
                "    \"type\": \"category\",\n" +
                "    \"data\": [\"1\", \"2\", \"3\"]\n" +
                "  },\n" +
                "  \"yAxis\": {\n" +
                "    \"type\": \"value\"\n" +
                "  },\n" +
                "  \"series\": [\n" +
                "    {\n" +
                "      \"data\": [67, 88, 65],\n" +
                "      \"type\": \"bar\",\n" +
                "      \"showBackground\": true,\n" +
                "      \"backgroundStyle\": {\n" +
                "        \"color\": \"rgba(220, 220, 220, 0.8)\"\n" +  // 注意这里使用的是双引号
                "      }\n" +
                "    }\n" +
                "  ]\n" +
                "}\n" +
                "```\n\n" +
                "``text\n" +
                "根据提供的数据，我们进行了用户增长趋势分析：\n" +
                "1. 数据概览：数据涵盖了3天的用户数量...\n" +
                "2. 趋势分析：整体呈现... \n" +
                "```\n\n" +
                "请严格按照以上格式输出！";


//        // 在调用之前保存到数据库
//        Chart chart = new Chart();
//        chart.setName(name);
//        chart.setGoal(goal);
//        chart.setChartData(csvData);
//        chart.setChartType(chartType);
//        chart.setStatus("wait");
//        chart.setUserId(loginUser.getId());
//        boolean saveResult = chartService.save(chart);
//        ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");
        //todo 要处理任务队列满了以后，抛异常的情况
        // 5. 调用 AI
        // 使用自定义线程池异步处理

        String aiResult;
        try {
            aiResult = aiManager.doChat(Arrays.asList(
                    ChatAiUtils.Message.system(systemPrompt),
                    ChatAiUtils.Message.user(userInput.toString())
            ));
        } catch (Exception e) {
            log.error("调用AI服务失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI服务调用失败，请稍后重试");
        }

        // 6. 处理AI返回的特殊情况
        if (StringUtils.startsWith(aiResult, "AI处理失败：")) {
            log.warn("AI处理失败：{}", aiResult);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, aiResult);
        }

        // 7. 正则解析 AI 输出
        Pattern pattern = Pattern.compile(
                "``json\\s*([\\s\\S]*?)\\s*```\\s*``text\\s*([\\s\\S]*?)\\s*```",
                Pattern.DOTALL
        );
        Matcher matcher = pattern.matcher(aiResult);

        if (!matcher.find()) {
            log.error("AI返回内容无法解析：{}", aiResult);
            // 将原始内容保存到数据库以便后续分析
            Chart chart = new Chart();
            chart.setName(name);
            chart.setGoal(goal);
            chart.setChartData(csvData);
            chart.setChartType(chartType);
            chart.setGenChart("");
            chart.setGenResult("AI返回内容格式不符合预期，原始内容如下：\n" + aiResult);
            chart.setUserId(loginUser.getId());
            chartService.save(chart);

            BiResponse biResponse = new BiResponse();
            biResponse.setChartId(chart.getId());
            biResponse.setGenChart("");
            biResponse.setGenResult("AI返回内容格式错误，请联系管理员处理。您可以稍后重试或提供更清晰的分析需求");
            return ResultUtils.success(biResponse);
        }

        String genChart = matcher.group(1).trim();   // ECharts option JS 代码
        String genResult = matcher.group(2).trim();  // 分析结论文本

        // 8. 验证生成的图表代码是否为有效的JSON
        try {
            GSON.fromJson(genChart, Object.class);
        } catch (Exception e) {
            log.error("AI生成的图表代码不是有效的JSON格式：{}", genChart, e);
            // 保存错误信息到数据库
            Chart chart = new Chart();
            chart.setName(name);
            chart.setGoal(goal);
            chart.setChartData(csvData);
            chart.setChartType(chartType);
            chart.setGenChart(genChart);
            chart.setGenResult("AI生成的图表代码不是有效的JSON格式，原始分析结论如下：\n" + genResult + "\n\n错误信息：" + e.getMessage());
            chart.setUserId(loginUser.getId());
            chartService.save(chart);

            BiResponse biResponse = new BiResponse();
            biResponse.setChartId(chart.getId());
            biResponse.setGenChart("");
            biResponse.setGenResult("AI生成的图表代码格式有误，请联系管理员处理。您可以稍后重试或提供更清晰的分析需求");
            return ResultUtils.success(biResponse);
        }

        // 9. 保存到数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(csvData);
        chart.setChartType(chartType);
        chart.setGenChart(genChart);
        chart.setGenResult(genResult);
        chart.setUserId(loginUser.getId());
        chart.setStatus("succeed");
        boolean saveResult1 = chartService.save(chart);
        ThrowUtils.throwIf(!saveResult1, ErrorCode.SYSTEM_ERROR, "图表保存失败");


//        // 10. 为图表创建独立的数据表并存储原始数据
//        try {
//            chartService.createDataTableForChart(chart.getId(), csvData);
//
//            // 新增：插入数据到图表数据表
//            List<String> insertSqlList = SqlGeneratorUtils.generateInsertSQLList(chart.getId(), csvData);
//            for (String insertSql : insertSqlList) {
//                chartService.executeInsertQuery(insertSql);
//            }
//        } catch (Exception e) {
//            log.error("为图表创建数据表失败，chartId: {}", chart.getId(), e);
//            // 这里可以选择是否回滚图表保存操作，或者只是记录日志
//        }
        // 10. 构建响应结果
        BiResponse biResponse = new BiResponse();
        biResponse.setChartId(chart.getId());
        biResponse.setGenChart(genChart);
        biResponse.setGenResult(genResult);

        log.info("成功生成图表，ID: {}, 用户: {}", chart.getId(), loginUser.getId());
        return ResultUtils.success(biResponse);
    }

    /**
     * 智能分析异步
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @PostMapping("/gen/async")
    public BaseResponse<BiResponse> genChartByAIAsync(
            @RequestPart("file") MultipartFile multipartFile,
            GenChartByAiRequest genChartByAiRequest,
            HttpServletRequest request) {

        // 1. 获取请求参数
        String name = genChartByAiRequest.getName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();

        // 2. 参数校验
        ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "分析目标不能为空");
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "图表名称不能超过100个字符");

        //校验文件
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename();
        User loginUser = userService.getLoginUser(request);

        //限流, 用户每秒限流 5 个请求
        redisLimiterManager.doRateLimit("genChartByAi_" + loginUser.getId());
        //校验文件大小
        final long ONE_MB = 1024 * 1024L;
        ThrowUtils.throwIf(fileSize > ONE_MB, ErrorCode.PARAMS_ERROR, "文件大小不能超过1MB");
        //校验文件后缀
        ThrowUtils.throwIf(!Arrays.asList("xlsx", "xls", "csv").contains(FileUtil.getSuffix(fileName)), ErrorCode.PARAMS_ERROR, "文件类型错误");

        // 3. 构造用户输入（用于发送给 AI）
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求：\n").append(goal).append("\n");
        if (StringUtils.isNotBlank(chartType)) {
            userInput.append("图表类型：").append(chartType).append("\n");
        }
        String csvData = ExcelUtils.excelToCsv(multipartFile);
        userInput.append("原始数据：\n").append(csvData);

        // 4. 定义系统提示词（优化后的 Prompt）
        String systemPrompt = "你是一个专业的数据分析师和前端可视化专家，请根据用户提供的分析需求和原始数据，完成以下任务：\n\n" +
                "### 输入格式\n" +
                "分析需求：\n" +
                "{目标描述}\n" +
                "图表类型（可选）：\n" +
                "{line, bar, pie 等}\n" +
                "原始数据：\n" +
                "{CSV 格式数据，以逗号分隔}\n\n" +

                "### 输出格式要求（必须严格遵守！）\n" +
                "请按顺序输出以下两个部分，使用标准 Markdown 代码块包裹，禁止添加任何解释、标题或额外文本：\n\n" +

                "``json\n" +
                "{ECharts V5 的 option 配置对象，必须是严格的JSON格式，所有字符串必须使用双引号，不要使用单引号或任何JavaScript语法！}\n" +
                "```\n\n" +

                "``text\n" +
                "{详细的数据分析结论，越详细越好，包含趋势、极值、异常、建议等，不要出现代码}\n" +
                "```\n\n" +

                "### 重要规则说明\n" +
                "- 必须先输出 ECharts 配置，再输出分析结论。\n" +
                "- 必须使用上述代码块标记（```json 和 ```text），不要使用其他符号。\n" +
                "- 禁止输出任何额外内容（如“好的”、“已生成”、“注意：”等引导语）。\n" +
                "- ECharts配置必须是严格的JSON格式，所有字符串必须使用双引号\"\"而不是单引号''！\n" +
                "- option 中不要包含 console.log、click 事件或其他副作用代码。\n" +
                "- 图表标题应简洁明确，反映分析目标。\n" +
                "- 自动识别异常值或突变点并在分析中说明。\n" +
                "- 如果遇到无法处理的请求，直接返回：`AI处理失败：{具体错误原因}`\n\n" +

                "### 示例格式（注意JSON格式和双引号的使用）\n" +
                "正确的JSON格式示例：\n" +
                "``json\n" +
                "{\n" +
                "  \"title\": {\n" +
                "    \"text\": \"用户增长趋势\",\n" +
                "    \"left\": \"center\"\n" +
                "  },\n" +
                "  \"tooltip\": {\n" +
                "    \"trigger\": \"axis\"\n" +
                "  },\n" +
                "  \"xAxis\": {\n" +
                "    \"type\": \"category\",\n" +
                "    \"data\": [\"1\", \"2\", \"3\"]\n" +
                "  },\n" +
                "  \"yAxis\": {\n" +
                "    \"type\": \"value\"\n" +
                "  },\n" +
                "  \"series\": [\n" +
                "    {\n" +
                "      \"data\": [67, 88, 65],\n" +
                "      \"type\": \"bar\",\n" +
                "      \"showBackground\": true,\n" +
                "      \"backgroundStyle\": {\n" +
                "        \"color\": \"rgba(220, 220, 220, 0.8)\"\n" +  // 注意这里使用的是双引号
                "      }\n" +
                "    }\n" +
                "  ]\n" +
                "}\n" +
                "```\n\n" +
                "``text\n" +
                "根据提供的数据，我们进行了用户增长趋势分析：\n" +
                "1. 数据概览：数据涵盖了3天的用户数量...\n" +
                "2. 趋势分析：整体呈现... \n" +
                "```\n\n" +
                "请严格按照以上格式输出！";


        // 在调用之前保存到数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(csvData);
        chart.setChartType(chartType);
        chart.setStatus("wait");
        chart.setUserId(loginUser.getId());
        boolean saveResult = chartService.save(chart);
        ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");
        //todo 要处理任务队列满了以后，抛异常的情况
        // 5. 调用 AI
        // 使用自定义线程池异步处理
        // 使用自定义线程池异步处理
        CompletableFuture.runAsync(() -> {
            // 更新状态为"running"
            Chart updateChart = new Chart();
            updateChart.setId(chart.getId());
            updateChart.setStatus("running");
            boolean b = chartService.updateById(updateChart);
            if (!b) {
                handleChartUpdateError(chart.getId(), "更新图表执行中状态失败");
                return;
            }

            // 执行AI调用等耗时操作
            String aiResult;
            try {
                aiResult = aiManager.doChat(Arrays.asList(
                        ChatAiUtils.Message.system(systemPrompt),
                        ChatAiUtils.Message.user(userInput.toString())
                ));
            } catch (Exception e) {
                log.error("调用AI服务失败", e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI服务调用失败，请稍后重试");

            }
            log.info(aiResult);
            // 7. 正则解析 AI 输出
            Pattern pattern = Pattern.compile(
                    "``json\\s*([\\s\\S]*?)\\s*```\\s*``text\\s*([\\s\\S]*?)\\s*```",
                    Pattern.DOTALL
            );
            Matcher matcher = pattern.matcher(aiResult);
            // 检查是否匹配成功
            if (!matcher.find()) {
                log.error("AI返回内容无法解析：{}", aiResult);
                handleChartUpdateError(chart.getId(), "AI返回内容格式不符合预期");
                return;
            }
            String genChart = matcher.group(1).trim();   // ECharts option JS 代码
            String genResult = matcher.group(2).trim();  // 分析结论文本
            log.info("genChart: " + genChart);
            log.info("genResult: " + genResult);
            // 8. 验证生成的图表代码是否为有效的JSON
            try {
                GSON.fromJson(genChart, Object.class);
            } catch (Exception e) {
                log.error("AI生成的图表代码不是有效的JSON格式：{}", genChart, e);
                updateChart.setStatus("failed");
                updateChart.setExecMessage("AI生成的图表代码格式有误，请联系管理员处理");
                boolean updateResult = chartService.updateById(updateChart);
                if (!updateResult) {
                    handleChartUpdateError(chart.getId(), "更新图表失败状态失败");
                    return;
                }
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "AI生成的图表代码格式有误，请联系管理员处理");

            }
            // ... AI调用逻辑 ...

            // 处理结果并更新状态为"succeed"
            updateChart.setStatus("succeed");
            updateChart.setId(chart.getId());
            updateChart.setGenChart(genChart);
            updateChart.setGenResult(genResult);
            log.info("成功生成图表，ID: {}, 用户: {}", chart.getId(), loginUser.getId());
            boolean updateResult = chartService.updateById(updateChart);
            if (!updateResult) {
                handleChartUpdateError(chart.getId(), "更新图表成功状态失败");
            }


        }, threadPoolExecutor);

//        // 6. 处理AI返回的特殊情况
//        if (StringUtils.startsWith(aiResult, "AI处理失败：")) {
//            log.warn("AI处理失败：{}", aiResult);
//            throw new BusinessException(ErrorCode.SYSTEM_ERROR, aiResult);
//        }


//        // 10. 为图表创建独立的数据表并存储原始数据
//        try {
//            chartService.createDataTableForChart(chart.getId(), csvData);
//
//            // 新增：插入数据到图表数据表
//            List<String> insertSqlList = SqlGeneratorUtils.generateInsertSQLList(chart.getId(), csvData);
//            for (String insertSql : insertSqlList) {
//                chartService.executeInsertQuery(insertSql);
//            }
//        } catch (Exception e) {
//            log.error("为图表创建数据表失败，chartId: {}", chart.getId(), e);
//            // 这里可以选择是否回滚图表保存操作，或者只是记录日志
//        }
        // 10. 构建响应结果
        BiResponse biResponse = new BiResponse();
        biResponse.setChartId(chart.getId());
        log.info("成功提交任务，ID: {}, 用户: {}", chart.getId(), loginUser.getId());
        return ResultUtils.success(biResponse);
    }


    /**
     * 智能分析(异步消息队列)
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @PostMapping("/gen/async/mq")
    public BaseResponse<BiResponse> genChartByAIAsyncMq(
            @RequestPart("file") MultipartFile multipartFile,
            GenChartByAiRequest genChartByAiRequest,
            HttpServletRequest request) {

        // 1. 获取请求参数
        String name = genChartByAiRequest.getName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();

        // 2. 参数校验
        ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, "分析目标不能为空");
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "图表名称不能超过100个字符");

        //校验文件
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename();
        User loginUser = userService.getLoginUser(request);

        //限流, 用户每秒限流 5 个请求
        redisLimiterManager.doRateLimit("genChartByAi_" + loginUser.getId());
        //校验文件大小
        final long ONE_MB = 1024 * 1024L;
        ThrowUtils.throwIf(fileSize > ONE_MB, ErrorCode.PARAMS_ERROR, "文件大小不能超过1MB");
        //校验文件后缀
        ThrowUtils.throwIf(!Arrays.asList("xlsx", "xls", "csv").contains(FileUtil.getSuffix(fileName)), ErrorCode.PARAMS_ERROR, "文件类型错误");

        // 3. 构造用户输入（用于发送给 AI）
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求：\n").append(goal).append("\n");
        if (StringUtils.isNotBlank(chartType)) {
            userInput.append("图表类型：").append(chartType).append("\n");
        }
        String csvData = ExcelUtils.excelToCsv(multipartFile);
        userInput.append("原始数据：\n").append(csvData);

        // 4. 定义系统提示词（优化后的 Prompt）
        String systemPrompt = "你是一个专业的数据分析师和前端可视化专家，请根据用户提供的分析需求和原始数据，完成以下任务：\n\n" +
                "### 输入格式\n" +
                "分析需求：\n" +
                "{目标描述}\n" +
                "图表类型（可选）：\n" +
                "{line, bar, pie 等}\n" +
                "原始数据：\n" +
                "{CSV 格式数据，以逗号分隔}\n\n" +

                "### 输出格式要求（必须严格遵守！）\n" +
                "请按顺序输出以下两个部分，使用标准 Markdown 代码块包裹，禁止添加任何解释、标题或额外文本：\n\n" +

                "``json\n" +
                "{ECharts V5 的 option 配置对象，必须是严格的JSON格式，所有字符串必须使用双引号，不要使用单引号或任何JavaScript语法！}\n" +
                "```\n\n" +

                "``text\n" +
                "{详细的数据分析结论，越详细越好，包含趋势、极值、异常、建议等，不要出现代码}\n" +
                "```\n\n" +

                "### 重要规则说明\n" +
                "- 必须先输出 ECharts 配置，再输出分析结论。\n" +
                "- 必须使用上述代码块标记（```json 和 ```text），不要使用其他符号。\n" +
                "- 禁止输出任何额外内容（如“好的”、“已生成”、“注意：”等引导语）。\n" +
                "- ECharts配置必须是严格的JSON格式，所有字符串必须使用双引号\"\"而不是单引号''！\n" +
                "- option 中不要包含 console.log、click 事件或其他副作用代码。\n" +
                "- 图表标题应简洁明确，反映分析目标。\n" +
                "- 自动识别异常值或突变点并在分析中说明。\n" +
                "- 如果遇到无法处理的请求，直接返回：`AI处理失败：{具体错误原因}`\n\n" +

                "### 示例格式（注意JSON格式和双引号的使用）\n" +
                "正确的JSON格式示例：\n" +
                "``json\n" +
                "{\n" +
                "  \"title\": {\n" +
                "    \"text\": \"用户增长趋势\",\n" +
                "    \"left\": \"center\"\n" +
                "  },\n" +
                "  \"tooltip\": {\n" +
                "    \"trigger\": \"axis\"\n" +
                "  },\n" +
                "  \"xAxis\": {\n" +
                "    \"type\": \"category\",\n" +
                "    \"data\": [\"1\", \"2\", \"3\"]\n" +
                "  },\n" +
                "  \"yAxis\": {\n" +
                "    \"type\": \"value\"\n" +
                "  },\n" +
                "  \"series\": [\n" +
                "    {\n" +
                "      \"data\": [67, 88, 65],\n" +
                "      \"type\": \"bar\",\n" +
                "      \"showBackground\": true,\n" +
                "      \"backgroundStyle\": {\n" +
                "        \"color\": \"rgba(220, 220, 220, 0.8)\"\n" +  // 注意这里使用的是双引号
                "      }\n" +
                "    }\n" +
                "  ]\n" +
                "}\n" +
                "```\n\n" +
                "``text\n" +
                "根据提供的数据，我们进行了用户增长趋势分析：\n" +
                "1. 数据概览：数据涵盖了3天的用户数量...\n" +
                "2. 趋势分析：整体呈现... \n" +
                "```\n\n" +
                "请严格按照以上格式输出！";


        // 在调用之前保存到数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(csvData);
        chart.setChartType(chartType);
        chart.setStatus("wait");
        chart.setUserId(loginUser.getId());
        boolean saveResult = chartService.save(chart);
        ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");
        long newChartId = chart.getId();
        //todo 要处理任务队列满了以后，抛异常的情况
        // 5. 调用 AI
        // 使用自定义线程池异步处理
        // 使用自定义线程池异步处理
        //消息队列发的是表格的id
        biMessageProducer.sendMessage(String.valueOf(newChartId));
//        // 6. 处理AI返回的特殊情况
//        if (StringUtils.startsWith(aiResult, "AI处理失败：")) {
//            log.warn("AI处理失败：{}", aiResult);
//            throw new BusinessException(ErrorCode.SYSTEM_ERROR, aiResult);
//        }


//        // 10. 为图表创建独立的数据表并存储原始数据
//        try {
//            chartService.createDataTableForChart(chart.getId(), csvData);
//
//            // 新增：插入数据到图表数据表
//            List<String> insertSqlList = SqlGeneratorUtils.generateInsertSQLList(chart.getId(), csvData);
//            for (String insertSql : insertSqlList) {
//                chartService.executeInsertQuery(insertSql);
//            }
//        } catch (Exception e) {
//            log.error("为图表创建数据表失败，chartId: {}", chart.getId(), e);
//            // 这里可以选择是否回滚图表保存操作，或者只是记录日志
//        }
        // 10. 构建响应结果
        BiResponse biResponse = new BiResponse();
        biResponse.setChartId(chart.getId());
        log.info("成功提交任务，ID: {}, 用户: {}", chart.getId(), loginUser.getId());
        return ResultUtils.success(biResponse);
    }
    
    /**
     * 重新生成图表
     *
     * @param chartId 图表ID
     * @param request HTTP请求
     * @return 重新生成结果
     */
    @PostMapping("/regenerate")
    public BaseResponse<BiResponse> regenerateChart(@RequestParam("chartId") long chartId, HttpServletRequest request) {
        // 参数校验
        if (chartId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "图表ID不能为空");
        }
        
        // 获取登录用户
        User loginUser = userService.getLoginUser(request);
        
        // 获取图表数据
        Chart oldChart = chartService.getById(chartId);
        if (oldChart == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图表不存在");
        }
        
        // 权限校验：只能重新生成自己的图表
        if (!oldChart.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        
        // 限流
        redisLimiterManager.doRateLimit("regenerateChart_" + loginUser.getId());
        
        // 更新图表状态为"wait"
        Chart updateChart = new Chart();
        updateChart.setId(chartId);
        updateChart.setStatus("wait");
        updateChart.setExecMessage("重新生成中...");
        boolean updateResult = chartService.updateById(updateChart);
        if (!updateResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新图表状态失败");
        }
        
        // 发送消息到消息队列重新处理
        biMessageProducer.sendMessage(String.valueOf(chartId));
        
        // 构建响应结果
        BiResponse biResponse = new BiResponse();
        biResponse.setChartId(chartId);
        log.info("成功提交重新生成任务，ID: {}, 用户: {}", chartId, loginUser.getId());
        return ResultUtils.success(biResponse);
    }

    private void handleChartUpdateError(long chartId, String execMessage) {
        Chart updateChart = new Chart();
        updateChart.setId(chartId);
        updateChart.setStatus("failed");
        updateChart.setExecMessage(execMessage);
        boolean updateResult = chartService.updateById(updateChart);
        if (!updateResult) {
            log.error("更新图表失败：{}", chartId + "," + execMessage);
        }
    }

    /**
     * 获取图表生成状态（用于前端轮询）
     *
     * @param id
     * @param request
     * @return
     */
    @GetMapping("/get/status")
    public BaseResponse<Chart> getChartStatus(long id, HttpServletRequest request) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        Chart chart = chartService.getById(id);
        if (chart == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        // 安全检查：确保用户只能查看自己的图表
        User loginUser = userService.getLoginUser(request);
        if (!chart.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }

        // 只返回必要的状态信息，避免泄露敏感数据
        Chart statusChart = new Chart();
        statusChart.setId(chart.getId());
        statusChart.setStatus(chart.getStatus());
        statusChart.setExecMessage(chart.getExecMessage());

        // 如果生成成功，也返回图表数据
        if ("succeed".equals(chart.getStatus())) {
            statusChart.setGenChart(chart.getGenChart());
            statusChart.setGenResult(chart.getGenResult());
        }

        return ResultUtils.success(statusChart);
    }

    /**
     * 获取查询包装类
     *
     * @param chartQueryRequest
     * @return
     */
    private QueryWrapper<Chart> getQueryWrapper(ChartQueryRequest chartQueryRequest) {
        QueryWrapper<Chart> queryWrapper = new QueryWrapper<>();
        if (chartQueryRequest == null) {
            return queryWrapper;
        }
        Long id = chartQueryRequest.getId();
        String name = chartQueryRequest.getName();
        String goal = chartQueryRequest.getGoal();
        String chartType = chartQueryRequest.getChartType();
        Long userId = chartQueryRequest.getUserId();
        String sortField = chartQueryRequest.getSortField();
        String sortOrder = chartQueryRequest.getSortOrder();

        queryWrapper.eq(id != null && id > 0, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(goal), "goal", goal);
        queryWrapper.like(StringUtils.isNotBlank(name), "name", name);
        queryWrapper.eq(StringUtils.isNotBlank(chartType), "chartType", chartType);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq("isDelete", false);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }


}